package birdsong.server;

import java.util.LinkedList;

import birdsong.comm.ServerComm;
import birdsong.comm.ServerSideMessage;


public class Server implements BirdsongServer{

	protected ServerComm serverComm;	
	private LinkedList<ServerClient> clients=new LinkedList<ServerClient>(); 

	@Override
	public void start(ServerComm serverComm) {


		this.serverComm=serverComm;
		//UNCOMMENT THIS AFTER TEST
		//clients=new LinkedList<ServerClient>();
		serverComm.start();

		while (true) {

			if(serverComm.hasNextMessage()){
				ServerSideMessage message = serverComm.getNextMessage();

				switch (message.getType()) {
				case CLIENT_CONNECTED:
					clientConnected(message.getClientNickname());
					break;
				case CLIENT_DISCONNECTED:
					clientDisconnected(message.getClientNickname());
					break;
				case BIRDSONG_MESSAGE:
					message(message.getClientNickname(), message.getPayload());
					break;
				case FOLLOW:
					follow(message.getClientNickname(), message.getPayload());
					break;
				case UNFOLLOW:
					unfollow(message.getClientNickname(), message.getPayload());
					break;

				default:
					break;
				}
			}
		}
	}


	////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////
	//THIS METHODS ARE PUBLIC JUST FOR TESTING
	////////////////////////////////////////////////////////////////////
	////////////////////////////////////////////////////////////////////


	/**
	 * Register the client in the server. If there is a user with the same name it will send a error message
	 */
	public void clientConnected(String nickName) {

		boolean userAlreadyExists = verifyClient(nickName);

		if(!userAlreadyExists){

			ServerClient newClient = new ServerClient(nickName);

			//sendCurrentOnlineUsersToNewClient(newClient);

			clients.add(newClient);
			

		}else{

			serverComm.sendError(nickName, "THE CHOOSEN NICKNAME ALREADY EXISTS");

		}
	}

	/**
	 * Send a message to all online user when a client connects to the server
	 * @param newClient
	 */
	private void sendCurrentOnlineUsersToNewClient(ServerClient newClient) {
		if(!clients.isEmpty())
			for (ServerClient onlineClients : clients) {

				//SEND TO CONNECTED USER THE NEW CLIENT
				serverComm.sendClientConnected(onlineClients.getNickName(), newClient.getNickName());

				//SEND TO NEW CLIENT THE CONNECTED USERS
				serverComm.sendClientConnected(newClient.getNickName(), onlineClients.getNickName());
			}

	}

	/**
	 * Verify if the list is empty and if there is a user with the same nickname
	 * @param nickName
	 * @return false if there is not an user with the same nickname or if the list is empty
	 */
	public boolean verifyClient(String string) {

		if(!clients.isEmpty())	
			for (ServerClient client : clients) {
				if(client.getNickName().equals(string)){
					return true;
				}
			}
		return false;

	}

	/**
	 * The client has to be removed from every list
	 * @param clientNickname
	 */
	public void clientDisconnected(String clientNickname) {

		ServerClient disconnetedClient = getClient(clientNickname, clients);

		if(disconnetedClient!=null){

			clients.remove(disconnetedClient);

			//THIS IS COMMENTED BECAUSE WE DO NOT HAVE THE SERVERCOM
			//sendDisconnectedClient(clientNickname);

			for (ServerClient client : clients) {

				client.removeFollower(getClient(clientNickname, client.getFollowers()));

			}
		}

	}



	/**
	 * Send to current online users the information that the clientNickname has disconnected
	 * @param clientNickname
	 */
	private void sendDisconnectedClient(String clientNickname) {
		if(!clients.isEmpty())
			for (ServerClient onlineClients : clients) {

				serverComm.sendClientDisconnected(onlineClients.getNickName(), clientNickname);

			}
	}


	/**
	 * Get the client with certain nickname from a list of clients
	 * @param clientNickname
	 * @param clients 
	 * @return null if the list is empty or if the client not exist.
	 */
	public ServerClient getClient(String clientNickname, LinkedList<ServerClient> clients) {

		if(!clients.isEmpty())	
			for (ServerClient client : clients) {
				if(client.getNickName().equals(clientNickname)){

					return client;
				}
			}
		return null;
	}


	public void message(String clientNickname, String payload) {
		ServerClient client = getClient(clientNickname, clients);
		
		if(client!=null){
			for (ServerClient follower : client.getFollowers()) {
				serverComm.sendBirdsongMessage(client.getNickName(), follower.getNickName(), payload);

			}
		}
	}

	/**
	 * clientNickname will follow the clientToFollowNickname
	 * @param clientNickname
	 * @param clientToFollowNickname
	 */
	public void follow(String clientNickname, String clientToFollowNickname) {

		ServerClient client = getClient(clientNickname, clients);

		ServerClient clientToFollow = getClient(clientToFollowNickname, clients);

		if(client!=null && clientToFollow!=null){

			clientToFollow.addFollower(client);

			//			serverComm.sendNewFollower(clientToFollowNickname, clientNickname);


		}
	}


	public void unfollow(String clientNickname, String clientToUnfollowNickname) {
		ServerClient client = getClient(clientNickname, clients);
		
		ServerClient clientToUnfollow = getClient(clientToUnfollowNickname, clients);
		
		if(!client.equals(null) && !clientToUnfollow.equals(null)){	
			client.removeFollower(clientToUnfollow);
			serverComm.sendNewUnfollow(clientToUnfollow.getNickName(), client.getNickName());
		}
	}

	@Override
	public String toString() {
		return "Server [clients=" + clients.toString() + "]";
	}


}
